﻿
Ext.define('Ext.container.Container', {
    extend: 'Ext.Component',

    xtype: 'container',

    alternateClassName: [
        'Ext.Container',
        'Ext.AbstractContainer'
    ],

    requires: [
        'Ext.util.MixedCollection',
        'Ext.layout.container.Auto',
        'Ext.ZIndexManager'
    ],

    mixins: [
        'Ext.mixin.Queryable'
    ],

    renderTpl: '{%this.renderContainer(out,values)%}',


    isContainer: true,









    autoDestroy: true,






    defaultType: 'panel',


    detachOnRemove: true,




    layout: 'auto',


    suspendLayout: false,













    ariaRole: 'presentation',

    baseCls: Ext.baseCSSPrefix + 'container',


    layoutCounter: 0,


































    add: function () {
        var me = this,
            args = Ext.Array.slice(arguments),
            index = (typeof args[0] == 'number') ? args.shift() : -1,
            layout = me.getLayout(),
            needsLayout = false,
            addingArray, items, i, length, item, pos, ret,
            instanced;


        if (args.length == 1 && Ext.isArray(args[0])) {
            items = args[0];
            addingArray = true;
        } else {
            items = args;
        }

        if (me.rendered) {
            Ext.suspendLayouts();
        }

        ret = items = me.prepareItems(items, true);
        length = items.length;

        if (!addingArray && length == 1) {
            ret = items[0];
        }


        for (i = 0; i < length; i++) {
            item = items[i];
            if (!item) {
                Ext.Error.raise("Cannot add null item to Container with itemId/id: " + me.getItemId());
            }

            if (item.isDestroyed) {
                Ext.Error.raise("Cannot add destroyed item '" + item.getId() + "' to Container '" + me.getId() + "'");
            }

            pos = (index < 0) ? me.items.length : (index + i);
            instanced = !!item.instancedCmp;
            delete item.instancedCmp;

            if (item.floating) {
                me.floatingItems.add(item);
                item.onAdded(me, pos, instanced);
                delete item.initOwnerCt;
                if (me.hasListeners.add) {
                    me.fireEvent('add', me, item, pos);
                }
            } else if ((!me.hasListeners.beforeadd || me.fireEvent('beforeadd', me, item, pos) !== false) && me.onBeforeAdd(item) !== false) {
                me.items.insert(pos, item);
                item.onAdded(me, pos, instanced);
                delete item.initOwnerCt;
                me.onAdd(item, pos);
                layout.onAdd(item, pos);

                needsLayout = true;
                if (me.hasListeners.add) {
                    me.fireEvent('add', me, item, pos);
                }
            }
        }



        if (needsLayout) {
            me.updateLayout();
        }
        if (me.rendered) {
            Ext.resumeLayouts(true);
        }

        return ret;
    },

    onAdded: function (container, pos, instanced) {
        this.callParent(arguments);



        if (instanced) {
            Ext.ComponentManager.markReferencesDirty();
        }
    },

    onRemoved: function (destroying) {
        var refHolder;


        if (!destroying) {
            refHolder = this.lookupReferenceHolder();
            if (refHolder) {





                Ext.ComponentManager.markReferencesDirty();
                refHolder.clearReferences();
            }
        }
        this.callParent(arguments);
    },

    afterComponentLayout: function () {
        var floaters = this.floatingItems.items,
            floaterCount = floaters.length,
            i, floater;

        this.callParent(arguments);


        for (i = 0; i < floaterCount; i++) {
            floater = floaters[i];
            if (!floater.rendered && floater.autoShow) {
                floater.show();
            }
        }
    },


    afterLayout: function (layout) {
        var me = this,
            scrollManager = me.scrollManager;

        ++me.layoutCounter;

        if (scrollManager && me.layoutCounter > 1) {
            scrollManager.refresh();
        }

        if (me.hasListeners.afterlayout) {
            me.fireEvent('afterlayout', me, layout);
        }
    },

    onDestroy: function () {
        this.callParent();
        this.refs = null;
    },

    beforeDestroy: function () {
        var me = this,
            items = me.items,
            floatingItems = me.floatingItems,
            c;

        if (items) {
            while ((c = items.first())) {
                me.doRemove(c, true);
            }
        }

        if (floatingItems) {
            while ((c = floatingItems.first())) {
                me.doRemove(c, true);
            }
        }

        Ext.destroy(
            me.layout
        );
        me.callParent();
    },

    beforeRender: function () {
        var me = this,
            layout = me.getLayout(),
            targetCls;

        me.callParent();

        if (!layout.initialized) {
            layout.initLayout();
        }

        targetCls = layout.targetCls;

        if (targetCls) {
            me.applyTargetCls(targetCls);
        }
    },


    cascade: function (fn, scope, origArgs) {
        var me = this,
            cs = me.items ? me.items.items : [],
            len = cs.length,
            i = 0,
            c,
            args = origArgs ? origArgs.concat(me) : [me],
            componentIndex = args.length - 1;

        if (fn.apply(scope || me, args) !== false) {
            for (; i < len; i++) {
                c = cs[i];
                if (c.cascade) {
                    c.cascade(fn, scope, origArgs);
                } else {
                    args[componentIndex] = c;
                    fn.apply(scope || c, args);
                }
            }
        }
        return this;
    },


    contains: function (comp, deep) {
        var result = false;
        if (deep) {
            this.cascade(function (c) {

                if (c.contains && c.contains(comp)) {
                    result = true;
                    return false;
                }
            });
            return result;
        } else {
            return this.items.contains(comp) || this.floatingItems.contains(comp);
        }
    },


    disable: function () {
        this.callParent(arguments);

        var itemsToDisable = this.getChildItemsToDisable(),
            length = itemsToDisable.length,
            item, i;

        for (i = 0; i < length; i++) {
            item = itemsToDisable[i];

            if (item.resetDisable !== false && !item.disabled) {
                item.disable();
                item.resetDisable = true;
            }
        }

        return this;
    },


    doLayout: function () {
        this.updateLayout();
        return this;
    },


    enable: function () {
        this.callParent(arguments);

        var itemsToDisable = this.getChildItemsToDisable(),
            length = itemsToDisable.length,
            item, i;

        for (i = 0; i < length; i++) {
            item = itemsToDisable[i];

            if (item.resetDisable) {
                item.enable();
            }
        }

        return this;
    },


    getChildByElement: function (el, deep) {
        var item,
            itemEl,
            i = 0,
            it = this.getRefItems(),
            ln = it.length;

        el = Ext.getDom(el);
        for (; i < ln; i++) {
            item = it[i];
            itemEl = item.getEl();
            if (itemEl && ((itemEl.dom === el) || itemEl.contains(el))) {
                return (deep && item.getChildByElement) ? item.getChildByElement(el, deep) : item;
            }
        }
        return null;
    },


    getComponent: function (comp) {
        if (Ext.isObject(comp)) {
            comp = comp.getItemId();
        }

        var c = this.items.get(comp);


        if (!c && typeof comp != 'number') {
            c = this.floatingItems.get(comp);
        }

        return c;
    },


    getLayout: function () {
        var me = this,
            layout = me.layout;

        if (!layout || !layout.isLayout) {
            me.setLayout(layout);
        }

        return me.layout;
    },


    getRefItems: function (deep) {
        var me = this,
            items = me.items.items,
            len = items.length,
            i = 0,
            item,
            result = [];

        for (; i < len; i++) {
            item = items[i];
            result[result.length] = item;
            if (deep && item.getRefItems) {
                result.push.apply(result, item.getRefItems(true));
            }
        }


        items = me.floatingItems.items;
        len = items.length;
        for (i = 0; i < len; i++) {
            item = items[i];
            result[result.length] = item;
            if (deep && item.getRefItems) {
                result.push.apply(result, item.getRefItems(true));
            }
        }

        return result;
    },

    initComponent: function () {
        var me = this;

        me.callParent();

        me.getLayout();


        me.constructing = true;

        me.initItems();

        delete me.constructing;
    },


    getReferences: function () {
        var ComponentManager = Ext.ComponentManager;

        if (ComponentManager.referencesDirty) {
            ComponentManager.fixReferences();
        }

        return this.refs || null;
    },


    lookupReference: function (key) {
        var refs = this.getReferences();

        return (refs && refs[key]) || null;
    },


    initItems: function () {
        var me = this,
            items = me.items;

        if (!items || !items.isMixedCollection) {



            me.items = new Ext.util.AbstractMixedCollection(false, me.getComponentId);
            me.floatingItems = new Ext.util.MixedCollection(false, me.getComponentId);

            if (items) {
                if (!Ext.isArray(items)) {
                    items = [items];
                }

                me.add(items);
            }
        }
    },


    initInheritedState: function (inheritedState, inheritedStateInner) {
        var me = this,
            controller = me.controller,
            layout = me.layout,
            session = me.session,


            viewModel = me.viewModel,
            reference = me.reference,
            referenceHolder = me.referenceHolder;

        me.callParent([inheritedState, inheritedStateInner]);

        if (me.collapsed) {
            inheritedState.collapsed = true;
        }

        if (controller) {
            inheritedState.referenceHolder = controller;
            referenceHolder = true;
        } else if (referenceHolder) {
            inheritedState.referenceHolder = me;
        }

        if (referenceHolder) {
            inheritedState.referencePath = '';
        } else if (reference && me.isParentReference) {
            inheritedState.referencePath = me.referenceKey + '.';
        }

        if (session) {
            inheritedState.session = session;
        }

        if (viewModel) {
            inheritedState.viewModelPath = '';
        } else if (reference && me.isParentReference) {
            inheritedState.viewModelPath = me.viewModelKey + '.';
        }

        if (layout && layout.initInheritedState) {
            layout.initInheritedState(inheritedState, inheritedStateInner);
        }
    },


    insert: function (index, component) {
        var compIdx;
        if (component && component.isComponent) {
            compIdx = this.items.indexOf(component);
            if (compIdx !== -1) {
                return this.move(compIdx, index);
            }
        }
        return this.add(index, component);
    },


    isAncestor: function (possibleDescendant) {
        while (possibleDescendant) {
            if (possibleDescendant.ownerCt === this) {
                return true;
            }
            possibleDescendant = possibleDescendant.ownerCt;
        }
    },


    lookupComponent: function (comp) {
        if (!comp.isComponent) {
            if (typeof comp === 'string') {
                comp = Ext.ComponentManager.get(comp);
            } else {
                comp = Ext.ComponentManager.create(comp, this.defaultType);
            }
        }
        return comp;
    },


    move: function (fromIdx, toIdx) {
        var me = this,
            items = me.items,
            item;

        if (fromIdx.isComponent) {
            fromIdx = items.indexOf(fromIdx);
        }
        item = items.getAt(fromIdx);
        if (fromIdx !== toIdx) {
            item = items.removeAt(fromIdx);
            if (item === false) {
                return false;
            }
            items.insert(toIdx, item);
            me.onMove(item, fromIdx, toIdx);
            if (me.hasListeners.move) {
                me.fireEvent('move', me, item, fromIdx, toIdx);
            }
            me.updateLayout();
        }
        return item;
    },


    moveBefore: function (item, before) {
        this.layout.moveItemBefore(item, before);
    },


    moveAfter: function (item, after) {
        var me = this,
            items = me.items,
            layout = me.layout,
            index = after ? layout.getMoveAfterIndex(after) : 0,
            before = items.getAt(index);

        layout.moveItemBefore(item, before);
    },


    nextChild: function (child, selector) {
        var me = this,
            items = me.items,
            childIndex = items.indexOf(child),
            i = 0,
            len = items.length,
            result;

        if (childIndex !== -1) {
            if (selector) {
                for (; i < len; i++) {
                    result = items.getAt(childIndex + i);

                    if (!result || Ext.ComponentQuery.is(result, selector)) {
                        break;
                    }
                }
            } else {
                result = items.getAt(childIndex + 1);
            }

            if (!result && me.ownerCt) {
                result = me.ownerCt.nextChild(me, selector);
            }
        }
        return result;
    },


    onAdd: Ext.emptyFn,


    onBeforeAdd: function (item) {

        var owner = item.ownerCt;
        if (owner && owner !== this) {
            owner.remove(item, false);
        }
    },

    onMove: Ext.emptyFn,


    onRemove: Ext.emptyFn,

    onPosition: function () {
        this.callParent(arguments);
        this.repositionFloatingItems();
    },

    onResize: function () {
        this.callParent(arguments);
        this.repositionFloatingItems();
    },


    prevChild: function (child, selector) {
        var me = this,
            items = me.items,
            childIndex = items.indexOf(child),
            i = 0,
            len = items.length,
            result;

        if (childIndex !== -1) {
            if (selector) {
                for (; i < len; i++) {
                    result = items.getAt(childIndex - i);

                    if (!result || Ext.ComponentQuery.is(result, selector)) {
                        break;
                    }
                }
            } else {
                result = items.getAt(childIndex - 1);
            }

            if (!result && me.ownerCt) {
                result = me.ownerCt.nextChild(me, selector);
            }
        }
        return result;
    },


    remove: function (component, autoDestroy) {
        var me = this,
            c = me.getComponent(component);
        if (Ext.isDefined(Ext.global.console) && !c) {
            Ext.global.console.warn("Attempted to remove a component that does not exist. Ext.container.Container: remove takes an argument of the component to remove. cmp.remove() is incorrect usage.");
        }

        if (c && (!me.hasListeners.beforeremove || me.fireEvent('beforeremove', me, c) !== false)) {
            me.doRemove(c, autoDestroy);
            if (me.hasListeners.remove) {
                me.fireEvent('remove', me, c);
            }

            if (!me.destroying && !c.floating) {
                me.updateLayout();
            }
        }

        return c;
    },


    removeAll: function (autoDestroy) {
        var me = this,
            removeItems = me.items.items.slice().concat(me.floatingItems.items),
            items = [],
            i = 0,
            len = removeItems.length,
            item;


        Ext.suspendLayouts();
        me.removingAll = true;
        for (; i < len; i++) {
            item = removeItems[i];
            me.remove(item, autoDestroy);

            if (item.ownerCt !== me) {
                items.push(item);
            }
        }
        me.removingAll = false;

        Ext.resumeLayouts(!!len);
        return items;
    },


    setLayout: function (layout) {
        var me = this,
            currentLayout = me.layout,
            currentIsLayout = currentLayout && currentLayout.isLayout,
            protoLayout, type;

        if (typeof layout === 'string') {
            layout = {
                type: layout
            };
        }

        type = layout.type;

        if (currentIsLayout && (!type || (type === currentLayout.type))) {

            delete layout.type;
            currentLayout.setConfig(layout);
        } else {

            if (currentIsLayout) {
                currentLayout.setOwner(null);
            }

            protoLayout = me.self.prototype.layout;

            if (typeof protoLayout === 'string') {
                layout.type = type || protoLayout;
            } else {

                Ext.merge(Ext.merge({}, protoLayout), layout);
            }

            layout = this.layout = Ext.Factory.layout(layout);
            layout.setOwner(this);
        }

        if (me.rendered) {
            me.updateLayout();
        }
    },


    setActiveItem: function (item) {
        return this.getLayout().setActiveItem(item);
    },






    privates: {

        applyDefaults: function (config) {
            var defaults = this.defaults;

            if (defaults) {
                if (Ext.isFunction(defaults)) {
                    defaults = defaults.call(this, config);
                }

                if (Ext.isString(config)) {
                    config = Ext.ComponentManager.get(config);
                }
                Ext.applyIf(config, defaults);
            }
            return config;
        },

        applyReference: function (reference) {
            var len;

            if (reference && reference.charAt(len = reference.length - 1) === '>') {
                this.isParentReference = true;
                reference = reference.substring(0, len);
            }

            if (reference && !this.validIdRe.test(reference)) {
                Ext.Error.raise('Invalid reference "' + reference + '" for ' + this.getId() +
                    ' - not a valid identifier');
            }

            return reference;
        },






        applyTargetCls: function (targetCls) {
            this.addCls(targetCls);
        },


        attachReference: function (component) {
            var me = this,
                key, refs;


            if (me.destroying || me.isDestroyed) {
                return;
            }

            refs = me.refs || (me.refs = {});
            key = component.referenceKey;
            if (refs[key] && refs[key] !== component) {
                Ext.log.warn('Duplicate reference: "' + key + '" on ' + me.id);
            }
            refs[key] = component;
        },


        clearReference: function (component) {
            var refs = this.refs,
                key = component.referenceKey;

            if (refs && key) {



                component.viewModelKey = component.referenceKey = refs[key] = null;
            }
        },


        clearReferences: function () {
            this.refs = null;
        },


        detachComponent: function (component) {
            Ext.getDetachedBody().appendChild(component.getEl());
        },


        doRemove: function (component, doDestroy) {

            doDestroy = doDestroy === true || (doDestroy !== false && this.autoDestroy);

            var me = this,
                layout = me.layout,
                hasLayout = layout && me.rendered,


                isDestroying = component.destroying || doDestroy,
                floating = component.floating;

            if (floating) {
                me.floatingItems.remove(component);
            } else {
                me.items.remove(component);
            }


            if (hasLayout && !floating) {

                if (layout.running) {
                    Ext.Component.cancelLayout(component, isDestroying);
                }
                layout.onRemove(component, isDestroying);
            }

            component.onRemoved(isDestroying);

            me.onRemove(component, isDestroying);


            if (doDestroy) {
                component.destroy();
            }

            else {
                if (hasLayout && !floating) {
                    layout.afterRemove(component);
                }
                if (me.detachOnRemove && component.rendered) {
                    me.detachComponent(component);
                }
            }
        },

        finishRenderChildren: function () {
            this.callParent();

            var layout = this.getLayout();

            if (layout) {
                layout.finishRender();
            }
        },


        getChildItemsToDisable: function () {
            return this.query('[isFormField],button');
        },


        getComponentId: function (comp) {
            return comp.getItemId && comp.getItemId();
        },


        getContentTarget: function () {
            return this.getLayout().getContentTarget();
        },


        getDefaultContentTarget: function () {
            return this.el;
        },


        getFocusEl: function () {
            return this.getTargetEl();
        },

        getScrollerEl: function () {
            return this.layout.getScrollerEl() || this.callParent();
        },


        prepareItems: function (items, applyDefaults) {


            if (Ext.isArray(items)) {
                items = items.slice();
            } else {
                items = [items];
            }


            var me = this,
                i = 0,
                len = items.length,
                item;

            for (; i < len; i++) {
                item = items[i];
                if (item == null) {
                    Ext.Array.erase(items, i, 1);
                    --i;
                    --len;
                } else {
                    if (applyDefaults) {
                        item = this.applyDefaults(item);
                    }


                    item.initOwnerCt = me;
                    if (item.isComponent) {



                        item.instancedCmp = true;
                    }
                    items[i] = me.lookupComponent(item);


                    delete item.initOwnerCt;
                }
            }

            return items;
        },

        repositionFloatingItems: function () {
            var floaters = this.floatingItems.items,
                floaterCount = floaters.length,
                i, floater;


            for (i = 0; i < floaterCount; i++) {
                floater = floaters[i];
                if (floater.el && !floater.hidden) {
                    floater.setPosition(floater.x, floater.y);
                }
            }
        },

        _noMargin: {
            'margin-top': '',
            'margin-right': '',
            'margin-bottom': '',
            'margin-left': ''
        },



        resetItemMargins: function () {
            var items = this.items.items,
                i = items.length,
                noMargin = this._noMargin,
                item;

            while (i--) {
                item = items[i];
                item.margin$ = null;
                item.el.setStyle(noMargin);
            }
        },

        setupRenderTpl: function (renderTpl) {
            this.callParent(arguments);
            this.getLayout().setupRenderTpl(renderTpl);
        }
    }
});


